package cn.tlb.utils;

import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Matrix;
import android.os.Environment;
import android.support.annotation.NonNull;
import android.util.Log;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.Date;

public class BitmapUtil {
    private static final String TAG = BitmapUtil.class.getSimpleName();
    /**
     * 保持图片原图宽高比的压缩
     * @param pathName  图片资源路径, 根据BitmapFactory.decodeXxx()方法,可以自由的选择解析图片的源
     * @param maxWidth  图片解析后最大宽
     * @param maxHeight 图片解析后最大高
     * @return
     */
    public static Bitmap decode(String pathName, int maxWidth, int maxHeight){
        //创建Options用于设置Bitmap解析的参数
        BitmapFactory.Options options = new BitmapFactory.Options();
        // 只解析边界(Just Decode Bounds), 目的是获取原图的宽高
        options.inJustDecodeBounds = true;
        // 读取Bitmap文件的文件头, 获取图片宽高保存到options中, 此时图片并没有被加载到内存(否则不是照样OOM?)
        BitmapFactory.decodeFile(pathName, options);

        //从options中读取原图宽高
        int originWidth = options.outWidth;
        int originHeight = options.outHeight;

        //计算要压缩的采样率, 取宽和高采样率的最大值
        int sampleOfHeight = (int) Math.ceil((float)originHeight/maxHeight);
        int sampleOfWidth = (int) Math.ceil((float)originWidth/maxWidth);
        int sample = Math.max(sampleOfHeight, sampleOfWidth);
        //设置采样率为sample,
        // 如果 sample>1 解析出来后,图片的实际大小为原图的  1/(sample*sample)
        // 如果 sample <= 1, 当做 sample = 1处理, 解析出来后,图片和实际大小一样
        options.inSampleSize = sample;

        //根据设定的采样率,解析图片资源为Bitmap(要得到Bitmap了)
        options.inJustDecodeBounds = false;
        return BitmapFactory.decodeFile(pathName, options);
    }

    private static final String TEMP_PATH = Environment.getExternalStorageDirectory() + "/4399MCPE/temp";
    private static final String FILENAME_JPG = new SimpleDateFormat("yyyyMMdd_HHmmssSSS").format(new Date()) + ".jpg";


    /**
     * 清空缓存目录
     * @return
     */
    public static boolean cleanTempDir(){
        try{
            File tempDir = new File(TEMP_PATH);
            cleanTempDir(tempDir);
            return tempDir.listFiles().length == 0;
        }catch(Exception e){
            e.printStackTrace();
            return false;
        }
    }

    private static void cleanTempDir(File parent){
        if(parent == null || !parent.exists()){
            return;
        }

        File[] files = parent.listFiles();
        if(files != null && files.length > 0) {
            for (int i = 0; i < files.length; i++) {
                File file = files[i];
                if (file.isDirectory()) {
                    cleanTempDir(file);
                } else {
                    file.delete();
                }
            }
        }else{
            parent.delete();
        }
    }

    /**
     * @param path      图片路径
     * @param maxWidth  目标图片最大宽(px)
     * @param maxHeight 目标图片最大高(px)
     * @param maxSizeInKB    目标图片最大大小(存储所需的空间)
     * @return 压缩后图片的缓存路径
     */
    public static String getCompressedPath(String path, int maxWidth, int maxHeight, int maxSizeInKB) throws IOException {
        File file;

        BitmapFactory.Options options = new BitmapFactory.Options();
        options.inJustDecodeBounds = true;
        BitmapFactory.decodeFile(path, options);

        //step 1: 小于给定尺寸, 压缩质量
        if(options.outWidth < maxWidth && options.outHeight < maxHeight){
            file = getCompressQualityFile(path, maxSizeInKB);
        }else {
            // 3.1: 按尺寸调整 inSampleSize 压缩
            file = getCompressSampleFile(path, maxWidth, maxHeight, options);

            //3.2   调整尺寸
            file = getCompressDimenFile(BitmapFactory.decodeFile(file.getAbsolutePath()), maxWidth, maxHeight);

            //3.3   调整质量
            file = getCompressQualityFile(file.getAbsolutePath(), maxSizeInKB);
        }
        return file.getAbsolutePath();
    }

    /**
     * @param bitmap      {@link Bitmap}对象
     * @param maxWidth  目标图片最大宽(px)
     * @param maxHeight 目标图片最大高(px)
     * @param maxSizeInKB    目标图片最大大小(存储所需的空间)
     * @return 压缩后图片的缓存路径
     */
    public static String getCompressedPath(Bitmap bitmap, int maxWidth, int maxHeight, int maxSizeInKB) throws IOException{
        File file;
        if(bitmap.getWidth() < maxWidth && bitmap.getHeight() < maxHeight){
            file = getCompressQualityFile(bitmap, maxSizeInKB);
        }else {
            file = getCompressDimenFile(bitmap, maxWidth, maxHeight);
            file = getCompressQualityFile(file.getAbsolutePath(), maxSizeInKB);
        }
        return file.getAbsolutePath();
    }

    @NonNull
    private static File getCompressSampleFile(String path, int maxWidth, int maxHeight, BitmapFactory.Options options) throws FileNotFoundException {
        options.inSampleSize = Math.min(options.outWidth / maxWidth, options.outHeight / maxHeight);
        options.inJustDecodeBounds = false;
        // 压缩后 options.outWidth >= maxWidth, options.outHeight >= maxHeight, 需要进一步压缩
        Bitmap bitmap = BitmapFactory.decodeFile(path, options);
        File file = new File(TEMP_PATH, FILENAME_JPG);
        bitmap.compress(Bitmap.CompressFormat.JPEG, 100, new FileOutputStream(file));
        recycleBitmap(bitmap);
        return file;
    }

    @NonNull
    private static File getCompressDimenFile(Bitmap bitmap, float maxWidth, float maxHeight) throws FileNotFoundException {
        Matrix matrix = new Matrix();
        float sx = Math.min(maxWidth / bitmap.getWidth(), maxHeight / bitmap.getHeight());
        matrix.postScale(sx, sx);
        bitmap = Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(), matrix, false);
        File file = new File(TEMP_PATH, FILENAME_JPG);
        bitmap.compress(Bitmap.CompressFormat.JPEG, 100, new FileOutputStream(file));
        recycleBitmap(bitmap);
        return file;
    }

    @NonNull
    private static File getCompressQualityFile(String path, int maxSizeInKB) throws FileNotFoundException {
        Bitmap bitmap = BitmapFactory.decodeFile(path);
            ByteArrayOutputStream baos = new ByteArrayOutputStream();
        int quality = 100;
        bitmap.compress(Bitmap.CompressFormat.JPEG, quality, baos);
        Log.i(TAG, String.format("size=%sBytes", baos.size()));
        boolean isCompressed = false;
        while (quality > 0 && baos.size() / 1024 > maxSizeInKB) {
            quality -= 5;
            baos.reset();
            bitmap.compress(Bitmap.CompressFormat.JPEG, quality, baos);
            Log.i(TAG, String.format("quality=%s,size=%sBytes", quality, baos.size()));
            isCompressed = true;
        }

        File file;
        if (isCompressed) {
            bitmap = BitmapFactory.decodeStream(new ByteArrayInputStream(baos.toByteArray()));
            file = new File(TEMP_PATH, FILENAME_JPG);
            bitmap.compress(Bitmap.CompressFormat.JPEG, quality, new FileOutputStream(file));
        }else{
            file = new File(path);
        }
        recycleBitmap(bitmap);
        return file;
    }

    @NonNull
    private static File getCompressQualityFile(Bitmap bitmap, int maxSizeInKB) throws FileNotFoundException {
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        int quality = 100;
        bitmap.compress(Bitmap.CompressFormat.JPEG, quality, baos);
        Log.i(TAG, String.format("size=%sBytes", baos.size()));
        boolean isCompressed = false;
        while (quality > 0 && baos.size() / 1024 > maxSizeInKB) {
            quality -= 5;
            baos.reset();
            bitmap.compress(Bitmap.CompressFormat.JPEG, quality, baos);
            Log.i(TAG, String.format("quality=%s,size=%sBytes", quality, baos.size()));
            isCompressed = true;
        }

        if (isCompressed) {
            bitmap = BitmapFactory.decodeStream(new ByteArrayInputStream(baos.toByteArray()));
        }

        File file = new File(TEMP_PATH, FILENAME_JPG);
        bitmap.compress(Bitmap.CompressFormat.JPEG, quality, new FileOutputStream(file));
        recycleBitmap(bitmap);
        return file;
    }

    private static void recycleBitmap(Bitmap bitmap) {
        if (bitmap != null && !bitmap.isRecycled()) {
            bitmap.recycle();
            bitmap = null;
            System.gc();
        }
    }

}
